//
// Create by Zeng Yun on 2018/12/20
//

package home

import (
	"adai.design/jarvis/common/log"
	"adai.design/jarvis/device"
	"adai.design/jarvis/home/model"
	"adai.design/jarvis/member"
	"encoding/json"
	"strings"
)

type Home struct {
	// 家庭消息中心
	hub *Hub

	// 家庭数据模型模型
	instance *model.HMHome

	// 容器
	containerManager *ContainerManager
	// 场景
	sceneManager *SceneManager
	// 家庭成员
	memberManager *MemberManager
	// 自动化
	automationManager *AutomationManager

	// 服务管理
	serviceManager *ServiceManager

	// 成员消息与设备消息
	dpkg chan *device.MessagePkg
	mpkg chan *member.MessagePkg
}

// 向设备推送消息
// home >> container
func (h *Home) postMessageToContainer(id string, msg *device.Message) error {
	pkg := &device.MessagePkg{
		DevId:  id,
		HomeId: h.instance.Id,
		Msg:    msg,
	}
	h.hub.sDevPkg <- pkg
	return nil
}

// 回复成员发送的请求消息
// home >> member
func (h *Home) replyMemberResult(pkg *member.MessagePkg) error {
	if pkg.Type == member.PkgTypeContext {
		member.SendPackage(pkg)
	} else {
		defer func() {
			if err := recover(); err != nil {
				log.Error("ack member request err: %s", err)
			}
		}()
		pkg.Ctx.Result <- pkg
	}
	return nil
}

// 容器消息处理
// container >> home
func (h *Home) containerPkgHandle(pkg *device.MessagePkg) {
	err := h.containerManager.handleContainerPkg(h, pkg)
	if err != nil {
		log.Error("%s", err)
	}
}

// 向家庭所有成员推送消息
// home >> members
func (h *Home) putMessageToMembers(msg *member.Message) error {

	for _, m := range h.instance.Members {
		pkg := &member.MessagePkg{
			Type: member.PkgTypeContext,
			Ctx: &member.Context{
				HomeId:   h.instance.Id,
				MemberId: m.Id,
			},
			Msg: msg,
		}
		msg.Home = h.instance.Id
		h.hub.sMemberPkg <- pkg
	}
	return nil
}

func (h *Home) pushNotificationToMembers(notify *member.APNSAlert) error {
	for _, m := range h.memberManager.members {
		member.PushNotificationToMember(m.Id, notify)
	}
	return nil
}

func (h *Home) updateAccessoryState(aid, devType string, state string) {
	h.serviceManager.handleAccessoryStateChanged(h, aid, devType, state)
}

func (h *Home) updateCharacteristic(chars ...*model.Characteristic) {
	// 推送给每一位家庭成员
	put := &member.Message{
		Path:   pathCharacteristic,
		Method: member.MsgMethodPut,
	}
	put.Data, _ = json.Marshal(chars)
	h.putMessageToMembers(put)

	// 服务状态推送检查
	for _, c := range chars {
		h.serviceManager.CheckNotify(h, c)
	}

	// TODO 自动化触发检查
}

func (h *Home) getRoomNameById(id string) string {
	for _, r := range h.instance.Rooms {
		if r.Id == id {
			return r.Name
		}
	}
	return "默认房间"
}

func (h *Home) updateMemberState(member *model.Member) {
	log.Info("member(%s) state(%s)", member.Id, member.State)
	h.automationManager.checkPositionTrigger(h, member)
	// todo 检查家庭成员位置变化是否需要推送更新给家庭每一个成员
}

func (h *Home) Save() error {
	h.instance.Containers = h.containerManager.containers
	h.instance.Scenes = h.sceneManager.scenes
	h.instance.Members = h.memberManager.members
	h.instance.Services = h.serviceManager.services
	return model.Homes.Update(h.instance)
}

func (h *Home) Load() error {
	h.containerManager.containers = h.instance.Containers
	h.sceneManager.scenes = h.instance.Scenes
	h.memberManager.members = h.instance.Members
	h.serviceManager.services = h.instance.Services
	h.automationManager.set(h.instance.Automations)
	return nil
}

// 成员消息处理
// member >> home
func (h *Home) memberPkgHandle(pkg *member.MessagePkg) {

	// path: home/container/*
	if strings.HasPrefix(pkg.Msg.Path, pathContainer) {
		err := h.containerManager.handleMemberPkg(h, pkg)
		if err != nil {
			log.Error("member pkg handle err: %s", err)
		}
		return
	}

	// path: home/characteristic
	if pkg.Msg.Path == pathCharacteristic {
		err := h.containerManager.handleMemberCharacteristic(h, pkg)
		if err != nil {
			log.Error("member pkg handle err: %s", err)
		}
		return
	}

	if pkg.Msg.Path == pathCharacteristicLog {
		err := handleMemberCharacteristicLog(h, pkg)
		if err != nil {
			log.Error("member pkg handle err: %s", err)
		}
		return
	}

	// path: home/scene
	if pkg.Msg.Path == pathScene {
		err := h.sceneManager.memberPkgHandle(h, pkg)
		if err != nil {
			log.Error("%s", err)
		}
		return
	}

	if strings.HasPrefix(pkg.Msg.Path, pathMember) {
		err := h.memberManager.handleMemberPkg(h, pkg)
		if err != nil {
			log.Error("%s", err)
		}
		return
	}

	// path: home/info
	if pkg.Msg.Path == pathHomeInfo {
		if pkg.Msg.Method == member.MsgMethodGet {
			data, _ := json.Marshal(h.instance.HomeInfo)
			ack := &member.MessagePkg{
				Type: pkg.Type,
				Ctx:  pkg.Ctx,
				Msg: &member.Message{
					Path:   pathHomeInfo,
					Method: member.MsgMethodGet,
					State:  "ok",
					Home:   h.instance.Id,
					Data:   data,
				},
			}
			_ = h.replyMemberResult(ack)
		}
		return
	}

	// path: home
	if pkg.Msg.Path == pathHome {
		if pkg.Msg.Method == member.MsgMethodGet {
			data, _ := json.Marshal(h.instance)
			ack := &member.MessagePkg{
				Type: pkg.Type,
				Ctx:  pkg.Ctx,
				Msg: &member.Message{
					Path:   pathHome,
					Method: member.MsgMethodGet,
					State:  "ok",
					Home:   h.instance.Id,
					Data:   data,
				},
			}
			_ = h.replyMemberResult(ack)
			return

		} else if pkg.Msg.Method == member.MsgMethodPost {
			var instance model.HMHome
			err := json.Unmarshal(pkg.Msg.Data, &instance)
			if err != nil {
				log.Error("home model parse err: %s", err)
				// 回复用户，更新成功
				ack := &member.MessagePkg{
					Type: pkg.Type,
					Ctx:  pkg.Ctx,
					Msg: &member.Message{
						Path:   pathHome,
						Method: member.MsgMethodPost,
						Home:   h.instance.Id,
						State:  "failed",
					},
				}
				_ = h.replyMemberResult(ack)
				return
			}

			// 重新加载家庭数据
			buf, _ := json.MarshalIndent(instance.Containers, "", "    ")
			log.Warn("containers: %s", string(buf))

			h.instance = &instance
			h.Load()
			h.Save()
			log.Warn("home(%s) reload", h.instance.Id)

			// 回复用户，更新成功
			data, err := json.Marshal(h.instance.HomeInfo)
			ack := &member.MessagePkg{
				Type: pkg.Type,
				Ctx:  pkg.Ctx,
				Msg: &member.Message{
					Path:   pathHomeInfo,
					Method: member.MsgMethodPost,
					Home:   h.instance.Id,
					State:  "ok",
					Data:   data,
				},
			}
			_ = h.replyMemberResult(ack)

			// 通知家庭中的每一个成员，家庭数据有更新
			msg := &member.Message{
				Path:   pathHomeInfo,
				Method: member.MsgMethodPut,
				Home:   h.instance.Id,
				Data:   data,
			}
			_ = h.putMessageToMembers(msg)
			return
		}
	}
}

func newHome(id string) *Home {
	instance, err := model.Homes.FindById(id)
	if err != nil {
		log.Error("home(%s) 404 not exist!", id)
		return nil
	}

	if instance == nil {
		return nil
	}
	home := &Home{
		instance:          instance,
		containerManager:  &ContainerManager{},
		sceneManager:      &SceneManager{},
		memberManager:     &MemberManager{},
		automationManager: &AutomationManager{},
		serviceManager:    &ServiceManager{},
	}
	_ = home.Load()
	return home
}

func (h *Home) run(hub *Hub) {
	h.hub = hub
	h.mpkg = make(chan *member.MessagePkg, 10)
	h.dpkg = make(chan *device.MessagePkg, 10)
	go func() {
		for {
			select {
			case pkg, ok := <-h.dpkg:
				if ok {
					h.containerPkgHandle(pkg)
				}

			case pkg, ok := <-h.mpkg:
				if ok {
					h.memberPkgHandle(pkg)
				}

			case ticker, ok := <-h.automationManager.ticker:
				if ok {
					h.automationManager.checkTimerTrigger(h, ticker)
				}

			}
		}
	}()
}
